package com.d4a5.mapping.google2
{
	import com.d4a5.mapping.core.AmfObject;
	import com.d4a5.mapping.core.MathStringParser;
	import com.d4a5.mapping.google.wmgBaseOverlay;
	import com.google.maps.overlays.PolygonOptions;
	
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.IEventDispatcher;
	import flash.events.TimerEvent;
	import flash.utils.Dictionary;
	import flash.utils.Timer;
	
	
	public class wmgOverlayLayerOptions implements IEventDispatcher 
	{
		
		
		protected var _calculationType:int = 0;
		public static const typeDefault:uint = 0;
		public static const typeClassBreakStrings:uint = 1;
		public static const typeClassBreakNumbers:uint = 2;
		public static const typeClassBreakEquations:uint = 3;
		public static const typeGradientNumbers:uint = 4;
		public static const typeGradientEquations:uint = 5;
		protected var _calculationFunctionPtr:Function;
		
		protected var _attribute:String;
		protected var _eventDispatcher:EventDispatcher;
		protected var _breakOptions:Array;
		protected var _breaks:Array;
		protected var _timer:Timer;
		protected var _hash:Dictionary;
		protected var _highlightedOptions:Object;
		protected var _bothOptions:Object;
		protected var _selectedOptions:Object;
		
		protected var _calculateBreaks:Boolean = true;

		
		
		public function wmgOverlayLayerOptions()
		{
			this._eventDispatcher = new EventDispatcher();
			
			this._hash = new Dictionary();
			this._calculationFunctionPtr = this.calc0;
			
			this._breakOptions = new Array();
			//this._breakOptions.addEventListener(CollectionEvent.COLLECTION_CHANGE,onChange);
			
			this._breaks = new Array();
			//this._breaks.addEventListener(CollectionEvent.COLLECTION_CHANGE,onChange);
			
			this._timer = new Timer( 100 , 0 );
			this._timer.addEventListener(TimerEvent.TIMER,onTimer);
		}
		
		protected function onChange( event:Event = null ) : void {
			this._timer.stop();
			this._timer.start();
		}
		
		protected function onTimer( event:Event ) : void {
			this._timer.stop();
			this._timer.reset();
			this.updateAllOptions();
		}
		
		
		public function get attribute() : String {
			return this._attribute;
		}
		public function set attribute( a:String ) : void {
			if( this._attribute != a ){
				this._attribute = a;
				this.onChange();
				//this.dispatchEvent( new Event( "options_changed" ) );
			}
		}
		
		public function get breaks() : Array {
			return this._breaks;
		}
		
		public function get breakOptions() : Array {
			return this._breakOptions;
		}
		
		public function get calculateBreaks() : Boolean {
			return this._calculateBreaks;
		}
		
		public function set calculateBreaks(state:Boolean) : void {
			this._calculateBreaks = state;
			this.onChange();
		}
		
		
		public function get highlightedOptions() : Object {
			return this._highlightedOptions;
		}
		public function set highlightedOptions( options:Object ) : void {
			if( this._highlightedOptions != options ){
				this._highlightedOptions = options;
				this.dispatchEvent( new Event( "state_options_changed" ) );
			}
		}
		public function get bothOptions() : Object {
			return this._bothOptions;
		}
		public function set bothOptions( options:Object ) : void {
			if( this._bothOptions != options ){
				this._bothOptions = options;
				this.dispatchEvent( new Event( "state_options_changed" ) );
			}
		}
		public function get selectedOptions() : Object {
			return this._selectedOptions;
		}
		public function set selectedOptions( options:Object ) : void {
			if( this._selectedOptions != options ){
				this._selectedOptions = options;
				this.dispatchEvent( new Event( "state_options_changed" ) );
			}
		}
		
		
		
		
		public function get calculationType() : int {
			return this._calculationType;
		}
		public function set calculationType( type:int ) : void {
			if( type >= 0 && type <= 5 ){
				this._calculationType = type;
				this._calculationFunctionPtr = this["calc" + type.toString() ] as Function;
				this.onChange();
			}
		}
		
		public function add( overlay:wmgOverlayLayerBaseShape ) : void {
			this._hash[ overlay ] = overlay.getDefaultOptions();
		}
		public function remove( overlay:wmgOverlayLayerBaseShape ) : void {
			if( this._hash[ overlay ] ){
				delete this._hash[ overlay ];
			}
		}
		
		public function updateAllOptions() : void {
			//want it divided up 
			var overlay:Object;
			
			if( this._calculateBreaks && this.breakOptions.length == 3 ){
				
				var min:Number = Number.POSITIVE_INFINITY;
				var max:Number = Number.NEGATIVE_INFINITY;
				var sum:Number = 0;
				var count:Number = 0;;
				
				for( overlay in this._hash ){
					var value:Number = MathStringParser.parse( this.attribute , overlay.attributes );
					if( isNaN(value) == false ){
						count++;
						sum += value;
						min = ( value < min ) ? value : min;
						max = ( value > max ) ? value : max;
					}
				}
				
				this._breaks.source[0] = min;
				this._breaks.source[1] = sum / count;
				this._breaks.source[2] = max;
			}
			
			
			for( overlay in this._hash ){
				this._hash[ overlay ] = this._calculationFunctionPtr( overlay );
			}
			this.dispatchEvent( new Event( "redraw_requested" ) );
		}
		
		public function getOptions( overlay:wmgOverlayLayerBaseShape ) : Object {
			return this.checkStateOfOverlay( overlay , this._hash[ overlay ] );
		}
		
		protected function checkStateOfOverlay( overlay:wmgOverlayLayerBaseShape , currentOptions:Object ) : Object {
			if( overlay.selected ){
				if( overlay.highlighted ){
					currentOptions = overlay.mergeOptions( [currentOptions, this._bothOptions ] );
				}
				else{
					currentOptions = overlay.mergeOptions( [currentOptions, this._selectedOptions ] );
				}
			}
			else if( overlay.highlighted ){
				currentOptions = overlay.mergeOptions( [currentOptions, this._highlightedOptions ] );
			}
			return currentOptions;
		}
		
		
		//typeDefault
		protected function calc0( overlay:wmgOverlayLayerBaseShape ) : Object {
			return overlay.getDefaultOptions();
		}
		//typeClassBreakStrings
		protected function calc1( overlay:wmgOverlayLayerBaseShape ) : Object {
			var value:String = overlay.attributes[ this.attribute ];
			if( value == null ){
				return overlay.getDefaultOptions();
			}
			
			var breaks:Array = this._breaks.toArray();
			var numBreaks:int = breaks.length;
			for( var i:int = 0; i < numBreaks; i++ ){
				if( breaks[i] == value ){
					return this._breakOptions[i];
				}
			}
			
			return overlay.getDefaultOptions();
		}
		//typeClassBreakNumbers
		protected function calc2( overlay:wmgOverlayLayerBaseShape ) : Object {
			var value:Number = Number( overlay.attributes[ this.attribute ] );
			if( isNaN( value ) ){
				return overlay.getDefaultOptions();
			}
			
			var breaks:Array = this._breaks.toArray();
			var numBreaks:int = breaks.length;
			if( numBreaks > 0 || value > breaks[0] ){
				for( var i:int = 1; i < numBreaks; i++ ){
					if( value < breaks[i] ){
						return this._breakOptions[i-1];
					}
				}
			}
			return overlay.getDefaultOptions();
		}
		//typeClassBreakEquations
		protected function calc3( overlay:wmgOverlayLayerBaseShape ) : Object {
			var value:Number = MathStringParser.parse( this.attribute , overlay.attributes );
			overlay.attributes.__calculatedValueFromEquation = value;
			if( isNaN( value ) ){
				return overlay.getDefaultOptions();
			}
			
			var breaks:Array = this._breaks.toArray();
			var numBreaks:int = breaks.length;
			if( numBreaks > 0 || value > breaks[0] ){
				for( var i:int = 1; i < numBreaks; i++ ){
					if( value < breaks[i] ){
						return this._breakOptions[i-1];
					}
				}
			}
			return overlay.getDefaultOptions();
		}
		//typeGradientNumbers
		protected function calc4( overlay:wmgOverlayLayerBaseShape ) : Object {
			var value:Number = Number( overlay.attributes[ this.attribute ] );
			
			return this.calc4And5( overlay , value );
		}
		//typeGradientEquations
		protected function calc5( overlay:wmgOverlayLayerBaseShape ) : Object {
			var value:Number = MathStringParser.parse( this.attribute , overlay.attributes );
			overlay.attributes.__calculatedValueFromEquation = value;
			
			return this.calc4And5( overlay , value );
		}
		
		protected function calc4And5( overlay:wmgOverlayLayerBaseShape , value:Number ) : Object {
			if( isNaN( value ) ){
				return overlay.getDefaultOptions();
			}

			var breaks:Array = this._breaks.toArray();
			var numBreaks:int = breaks.length;
			if( numBreaks > 0 && value >= breaks[0] && value <= breaks[numBreaks-1] ){
				for( var i:int = 0; i < numBreaks; i++ ){
					if( value == breaks[i] ){
						return this.checkStateOfOverlay( overlay , this._breakOptions[i] );
					}
					else if( value < breaks[i] ){
						var pct:Number = (value-breaks[i-1])/(breaks[i] - breaks[i-1]);
						return this.calcForGradient( pct , this._breakOptions[i-1] , this._breakOptions[i] , overlay.getDefaultOptions() );
					}
				}
			}
			return overlay.getDefaultOptions();
		}
		
		
		protected function calcForGradient( pct:Number , min:Object , max:Object , defaultOptions:Object ) : Object {
			if( min.fillStyle && max.fillStyle ){
				defaultOptions.fillStyle.color = this.calculateColor( pct ,  min.fillStyle.color as int , max.fillStyle.color as int );
				defaultOptions.fillStyle.alpha = (pct * (Number(max.fillStyle.alpha) - Number(min.fillStyle.alpha))) + Number(min.fillStyle.alpha);
			}
			if( min.strokeStyle && max.strokeStyle ){
				defaultOptions.strokeStyle.color = this.calculateColor( pct ,  min.strokeStyle.color as int , max.strokeStyle.color as int );
				defaultOptions.strokeStyle.alpha = (pct * (Number(max.strokeStyle.alpha) - Number(min.strokeStyle.alpha))) + Number(min.strokeStyle.alpha);
				defaultOptions.strokeStyle.thickness = (pct * (Number(max.strokeStyle.thickness) - Number(min.strokeStyle.thickness))) + Number(min.strokeStyle.thickness);
			}
			return defaultOptions;
		}
		
		protected function calculateColor( pct:Number , min:int , max:int ) : uint {
			var r:uint = 	    ((int(Number(((max & 0xff0000)>>16)-((min & 0xff0000)>>16)) * pct)+((min & 0xff0000)>>16)) << 16);
			var g:uint = 	  + ((int(Number(((max & 0xff00)>>8)   -((min & 0xff00)>>8))    * pct)+((min & 0xff00)>>8)) << 8);
			var b:uint = 	  +  (int(Number( (max & 0xff)         - (min & 0xff))          * pct)+ (min & 0xff));
			return r+g+b;
		}
		
		public function addEventListener(type:String, listener:Function, useCapture:Boolean=false, priority:int=0, useWeakReference:Boolean=false):void
		{
			this._eventDispatcher.addEventListener( type , listener , useCapture , priority , useWeakReference );
		}
		
		public function removeEventListener(type:String, listener:Function, useCapture:Boolean=false):void
		{
			this._eventDispatcher.removeEventListener( type , listener , useCapture );
		}
		
		public function dispatchEvent(event:Event):Boolean
		{
			return this._eventDispatcher.dispatchEvent( event );
		}
		
		public function hasEventListener(type:String):Boolean
		{
			return this._eventDispatcher.hasEventListener( type );
		}
		
		public function willTrigger(type:String):Boolean
		{
			return this._eventDispatcher.willTrigger( type );
		}
	}
}